import 'dart:convert';
import 'package:base_lib/base_lib.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import '../address_pickers.dart';
import 'address_point.dart';
import 'package:lpinyin/lpinyin.dart';
import '../entity/address_entity.dart' as address;

class AddressTree {
  static Map<String, AddressPoint> provinceInfoMap = Map();

  static Map<String, AddressPoint> cityInfoMap = Map();

  static Map<String, AddressPoint> areaInfoMap = Map();

  ///省  关联的城市 列表
  static Map<String, List<AddressPoint>> cityOfProvinceMap = Map();

  ///市 关联的 地区列表
  static Map<String, List<AddressPoint>> areaOfCityMap = Map();

  ///初始化数据Map
  static Future initAddressMapInfo() async {
    if (provinceInfoMap.isNotEmpty) return;

    String jsonStr = await rootBundle.loadString(
        'packages/${BaseLib.AssetsPackageName}/assets/location.json');

    address.AddressEntity addressEntity =
        address.AddressEntity.fromJson(json.decode(jsonStr));

    AddressPoint region2Point(address.Region region) {
      return AddressPoint(
        code: region.regionCode,
        parentCode: region.parentCode,
        name: region.name,
        letter: PinyinHelper.getFirstWordPinyin(region.name).substring(0, 1),
      );
    }

    provinceInfoMap.clear();
    addressEntity.province.forEach((element) {
      provinceInfoMap[element.regionCode] = region2Point(element);
    });

    cityInfoMap.clear();
    addressEntity.city.forEach((element) {
      cityInfoMap[element.regionCode] = region2Point(element);
    });

    areaInfoMap.clear();
    addressEntity.area.forEach((element) {
      areaInfoMap[element.regionCode] = region2Point(element);
    });

    printDataSize();
  }

  static printDataSize() {
    print(
        "${'￥￥￥地址数据' * 10}--${provinceInfoMap.length}--${cityInfoMap.length}--${areaInfoMap.length}");
  }
}

/// Province Class
class Provinces {
  // 是否将省份排序
  bool sort = true;

  Map<String, AddressPoint> provincesInfo;

  Map<String, AddressPoint> get provinceInfoMap =>
      this.provincesInfo ?? AddressTree.provinceInfoMap;

  Provinces({this.provincesInfo, this.sort});

  // 获取省份数据
  List<AddressPoint> get provinces {
    List<AddressPoint> provList = provinceInfoMap.values.toList();
    if (this.sort == true) {
      provList.sort((AddressPoint a, AddressPoint b) {
        return a.letter.compareTo(b.letter);
      });
    }
    return provList;
  }
}

/// tree point
class CityTree {
  /// the tree's modal
  /// data = AddressPoint(
  ///   letter,
  ///   name
  ///   parentCode,
  ///   code,
  ///   letter,
  ///   child: [
  ///     AddressPoint
  ///   ]
  /// )
  /// data = [
  ///   {
  ///     letter: 'S',
  ///     name: '四川',
  ///     code: 'xxxx'
  ///     parentCode:'xx'
  ///     child: [
  ///       letter: 'c',
  ///       code: 'xxxx'
  ///       parentCode:'xx'
  ///       name: '成都',
  ///       child
  ///     ]
  ///   }
  /// ]
  AddressPoint tree;
  Map<String, AddressPoint> provincesInfo;
  Map<String, AddressPoint> citiesInfo;
  Map<String, AddressPoint> areasInfo;

  CityTree({this.provincesInfo, this.citiesInfo, this.areasInfo});

  Map<String, AddressPoint> get provinceInfoMap =>
      this.provincesInfo ?? AddressTree.provinceInfoMap;

  Map<String, AddressPoint> get cityInfoMap =>
      this.citiesInfo ?? AddressTree.cityInfoMap;

  Map<String, AddressPoint> get areaInfoMap =>
      this.areasInfo ?? AddressTree.areaInfoMap;

  /// build tree by int provinceId,
  /// @param provinceId this is province id
  /// @return tree
  initTree(String provinceId) {
    AddressPoint root = provinceInfoMap[provinceId];
    List<AddressPoint> cityOfProvinceList =
        AddressTree.cityOfProvinceMap[provinceId];

    if (cityOfProvinceList == null) {
      cityOfProvinceList = cityInfoMap.values
          .where((element) => element.parentCode == provinceId)
          .toList();
      AddressTree.cityOfProvinceMap[provinceId] = cityOfProvinceList;
    }

    tree = _buildCities(root, cityOfProvinceList);

    return tree;
  }

  AddressPoint _buildCities(
      AddressPoint province, List<AddressPoint> cityOfTargetList) {
    if (province.child.isNotEmpty) return province;

    if (cityOfTargetList == null || cityOfTargetList.isEmpty) {
      return province;
    } else {
      for (int i = 0; i < cityOfTargetList.length; i++) {
        AddressPoint city = cityOfTargetList[i];

        ///直辖市
        if (cityOfTargetList.length == 1) {
          if (province.code == cityOfTargetList.first.code) {
            continue;
          }
        }

        List<AddressPoint> areaOfProvinceList =
            AddressTree.areaOfCityMap[city.code];
        if (areaOfProvinceList == null) {
          areaOfProvinceList = areaInfoMap.values
              .where((element) => element.parentCode == city.code)
              .toList();
          AddressTree.areaOfCityMap[province.code] = areaOfProvinceList;
        }

        _buildAreas(city, areaOfProvinceList);

        province.addChild(city);
      }
    }
    return province;
  }

  AddressPoint _buildAreas(
      AddressPoint cityTarget, List<AddressPoint> areaOfCityList) {
    if (cityTarget.child.isNotEmpty) return cityTarget;

    if (areaOfCityList == null || areaOfCityList.isEmpty) {
      return cityTarget;
    } else {
      areaOfCityList.forEach((area) {
        cityTarget.addChild(area);
      });
    }
    return cityTarget;
  }

  /// build tree by any code provinceId or cityCode or areaCode
  /// @param code build a tree
  /// @return Point a province with its cities and areas tree
  AddressPoint initTreeByCode(String code) {
    if (provinceInfoMap[code] != null) {
      return initTree(code);
    }

    //不是省code
    String provinceId = _getProvinceIdByCode(code);
    if (provinceId != null) {
      return initTreeByCode(provinceId);
    }
    return AddressPoint.nullPoint;
  }

  /// this is a private function, used the return to get a correct tree contain cities and areas
  /// @param code one of province city or area id;
  /// @return provinceId return id which province's child contain code
  String _getProvinceIdByCode(String code) {
    if (cityInfoMap[code] != null) {
      //城市code
      AddressPoint city = cityInfoMap[code];
      return city.parentCode;
    }

    if (areaInfoMap[code] != null) {
      //地区code
      AddressPoint area = areaInfoMap[code];
      return _getProvinceIdByCode(area.parentCode);
    }

    return null;
  }
}
